/*
 * @Author: 李无敌
 * @Date: 2025-01-27 10:00:00
 * @LastEditors: 李无敌
 * @LastEditTime: 2025-01-27 10:00:00
 * @FilePath: \nest-base\src\common\utils\csp-helper.ts
 * @Description: CSP配置工具类
 */

export interface CSPDirectives {
  defaultSrc?: string[];
  styleSrc?: string[];
  scriptSrc?: string[];
  fontSrc?: string[];
  imgSrc?: string[];
  connectSrc?: string[];
  frameSrc?: string[];
  objectSrc?: string[];
  mediaSrc?: string[];
  manifestSrc?: string[];
  workerSrc?: string[];
  formAction?: string[];
  baseUri?: string[];
  upgradeInsecureRequests?: string[];
  reportUri?: string[];
}

export interface CSPConfig {
  enabled: boolean;
  reportOnly: boolean;
  directives: CSPDirectives;
  crossOrigin?: {
    embedderPolicy?: boolean;
    openerPolicy?: string;
    resourcePolicy?: string;
  };
  referrerPolicy?: string;
}

export class CSPHelper {
  /**
   * 生成CSP指令字符串
   */
  static generateCSPString(directives: CSPDirectives): string {
    const parts: string[] = [];
    
    for (const [key, values] of Object.entries(directives)) {
      if (values && values.length > 0) {
        parts.push(`${key} ${values.join(' ')}`);
      }
    }
    
    return parts.join('; ');
  }

  /**
   * 验证CSP配置
   */
  static validateCSPConfig(config: CSPConfig): { valid: boolean; errors: string[] } {
    const errors: string[] = [];
    
    if (!config.enabled) {
      return { valid: true, errors: [] };
    }
    
    if (!config.directives) {
      errors.push('CSP directives 配置缺失');
    }
    
    // 检查必要的指令
    const requiredDirectives = ['defaultSrc'];
    for (const directive of requiredDirectives) {
      if (!config.directives[directive] || config.directives[directive].length === 0) {
        errors.push(`缺少必要的CSP指令: ${directive}`);
      }
    }
    
    return {
      valid: errors.length === 0,
      errors
    };
  }

  /**
   * 获取开发环境推荐的CSP配置
   */
  static getDevelopmentCSPConfig(): CSPConfig {
    return {
      enabled: true,
      reportOnly: true,
      directives: {
        defaultSrc: ["'self'", "http:", "https:", "data:", "blob:"],
        styleSrc: ["'self'", "'unsafe-inline'", "http:", "https:", "data:"],
        scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "http:", "https:"],
        fontSrc: ["'self'", "http:", "https:", "data:"],
        imgSrc: ["'self'", "data:", "http:", "https:", "blob:"],
        connectSrc: ["'self'", "ws:", "wss:", "http:", "https:"],
        frameSrc: ["'self'", "http:", "https:"],
        objectSrc: ["'self'", "http:", "https:"],
        mediaSrc: ["'self'", "http:", "https:"],
        manifestSrc: ["'self'", "http:", "https:"],
        workerSrc: ["'self'", "blob:", "http:", "https:"],
        formAction: ["'self'", "http:", "https:"],
        baseUri: ["'self'"]
      },
      crossOrigin: {
        embedderPolicy: false,
        openerPolicy: "unsafe-none",
        resourcePolicy: "cross-origin"
      },
      referrerPolicy: "no-referrer-when-downgrade"
    };
  }

  /**
   * 获取生产环境推荐的CSP配置
   */
  static getProductionCSPConfig(): CSPConfig {
    return {
      enabled: true,
      reportOnly: false,
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdn.jsdelivr.net"],
        scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://unpkg.com"],
        fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdn.jsdelivr.net"],
        imgSrc: ["'self'", "data:", "https:", "blob:"],
        connectSrc: ["'self'", "wss:", "https://api.github.com"],
        frameSrc: ["'none'"],
        objectSrc: ["'none'"],
        mediaSrc: ["'self'"],
        manifestSrc: ["'self'"],
        workerSrc: ["'self'", "blob:"],
        formAction: ["'self'"],
        baseUri: ["'self'"],
        upgradeInsecureRequests: []
      },
      crossOrigin: {
        embedderPolicy: false,
        openerPolicy: "same-origin-allow-popups",
        resourcePolicy: "cross-origin"
      },
      referrerPolicy: "strict-origin-when-cross-origin"
    };
  }

  /**
   * 合并CSP配置
   */
  static mergeCSPConfigs(base: CSPConfig, override: Partial<CSPConfig>): CSPConfig {
    return {
      ...base,
      ...override,
      directives: {
        ...base.directives,
        ...override.directives
      },
      crossOrigin: {
        ...base.crossOrigin,
        ...override.crossOrigin
      }
    };
  }

  /**
   * 添加CSP报告URI（用于监控CSP违规）
   */
  static addReportUri(directives: CSPDirectives, reportUri: string): CSPDirectives {
    return {
      ...directives,
      reportUri: [reportUri]
    };
  }

  /**
   * 检查CSP配置是否过于宽松
   */
  static checkCSPSecurityLevel(config: CSPConfig): { level: 'strict' | 'moderate' | 'loose'; warnings: string[] } {
    const warnings: string[] = [];
    let score = 0;
    
    if (!config.enabled) {
      return { level: 'loose', warnings: ['CSP未启用'] };
    }
    
    const directives = config.directives;
    
    // 检查defaultSrc
    if (directives.defaultSrc?.includes("'unsafe-inline'")) {
      warnings.push('defaultSrc包含unsafe-inline，存在安全风险');
      score -= 2;
    }
    
    // 检查scriptSrc
    if (directives.scriptSrc?.includes("'unsafe-eval'")) {
      warnings.push('scriptSrc包含unsafe-eval，存在安全风险');
      score -= 2;
    }
    
    // 检查frameSrc
    if (directives.frameSrc?.includes("'none'")) {
      score += 1;
    } else {
      warnings.push('frameSrc未设置为none，可能存在点击劫持风险');
    }
    
    // 检查objectSrc
    if (directives.objectSrc?.includes("'none'")) {
      score += 1;
    } else {
      warnings.push('objectSrc未设置为none，可能存在安全风险');
    }
    
    // 检查upgradeInsecureRequests
    if (directives.upgradeInsecureRequests) {
      score += 1;
    } else {
      warnings.push('未启用upgradeInsecureRequests，建议在生产环境启用');
    }
    
    // 检查reportOnly
    if (config.reportOnly) {
      warnings.push('CSP设置为reportOnly模式，不会阻止违规内容');
      score -= 1;
    }
    
    if (score >= 2) return { level: 'strict', warnings };
    if (score >= 0) return { level: 'moderate', warnings };
    return { level: 'loose', warnings };
  }
}
